﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ZackFlame.SimpleBrainFuck.Core
{
    /// <summary>
    /// Представляет транслятор SimpleBrainFuck-программы в BrainFuck-код.
    /// </summary>
    public sealed class SimpleCoder
    {
        static readonly SimpleParser parser = new SimpleParser();
        static readonly SimpleTranslator translator = new SimpleTranslator();

        readonly Regex trimEmptyLines = new Regex(@"\n\n+");
        readonly Regex multiCycles = new Regex(@"\[\[([^]]+)\]\]");
        readonly Regex multiZero = new Regex(@"(?:\[(?:\+|-)\]){2,}");
        readonly Regex zeroAfterCycle = new Regex(@"\]\[(?:\+|-)\]");

        /// <summary>
        /// Транслирует SimpleBrainFuck-программу в неоптимизированный BrainFuck-код.
        /// </summary>
        /// <param name="simpleCode">SimpleBrainFuck-программа.</param>
        /// <returns>BrainFuck-код.</returns>
        public string Translate(string simpleCode)
        {
            if (simpleCode == null)
                throw new ArgumentNullException("simpleCode");

            var tree = parser.BuildSemanticTree(simpleCode);

            string brainCode = trimEmptyLines.Replace(
                translator.RenderBrainFuck(tree), "\n");

            return brainCode.Trim('\n').Replace("\n", Environment.NewLine);
        }

        /// <summary>
        /// Оптимизирует BrainFuck-код.
        /// </summary>
        /// <param name="brainFuckCode">Неоптимизированный BrainFuck-код.</param>
        /// <returns>Оптимизированный BrainFuck-код.</returns>
        /// <remarks>
        /// Основные оптимизации - удаление бессмысленных переходов, вроде &lt;&lt;&gt;&gt;,
        /// а также сложений/вычитаний, например, ++--.
        /// </remarks>
        public string Optimize(string brainFuckCode)
        {
            if (brainFuckCode == null)
                throw new ArgumentNullException("brainFuckCode");

            if (brainFuckCode.Length <= 1)
                return brainFuckCode;

            string code = brainFuckCode;

            while (code.IndexOf("<>") != -1 || code.IndexOf("><") != -1 ||
                   code.IndexOf("+-") != -1 || code.IndexOf("-+") != -1)
            {
                code = code
                    .Replace("<>", string.Empty)
                    .Replace("><", string.Empty)
                    .Replace("+-", string.Empty)
                    .Replace("-+", string.Empty);
            }

            code = multiZero.Replace(code, SystemFunctions.Zero());
            code = zeroAfterCycle.Replace(code, "]");

            while (multiCycles.IsMatch(code))
            {
                code = multiCycles.Replace(code,
                    match => "[" + match.Groups[1] + "]");
            }

            return code;
        }

        /// <summary>
        /// Разбивает код на строки заданной длины (за исключением последней).
        /// </summary>
        /// <param name="brainFuckCode">Код для разбиения на строки.</param>
        /// <param name="lineLength">Длина строк результирующего кода.</param>
        /// <returns>Код, разбитый на строки заданной длины.</returns>
        public string SetLineLength(string brainFuckCode, int lineLength)
        {
            if (brainFuckCode == null)
                throw new ArgumentNullException("brainFuckCode");
            if (lineLength <= 0)
                throw new ArgumentOutOfRangeException("lineLength", "lineLength must be >= 0.");

            StringBuilder result = new StringBuilder(brainFuckCode);
            result.Replace(Environment.NewLine, string.Empty);

            int newLineLength = lineLength + Environment.NewLine.Length;
            for (int i = lineLength; i < result.Length; i += newLineLength)
            {
                result.Insert(i, Environment.NewLine);
            }

            return result.ToString();
        }
    }
}
